/*
 * sched.c - initializes struct for task 0
 */

#include <sched.h>
#include <mm.h>
#include <acces_modes.h>
#include <sys.h>

int set_eoi = 0;
struct protected_task_struct task[NR_TASKS]
  __attribute__((__section__(".data.task")));

struct task_struct * current()
{
  unsigned int pila;
  __asm__ __volatile (
    "movl %%esp,%0"
    : "=g" (pila));
  return (struct task_struct *)(pila&0xFFFFF000);
}

struct task_struct * list_head_to_task_struct(struct list_head * l)
{
  return list_entry(l,struct task_struct,p_list);
}

void task_switch(union task_union *t)
{
  /* Actualitzem la variable remaining_tics (requeriment de l'enunciat)*/
  remaining_tics = t->task.quantum;
  int i;
  /* Update TSS */
  tss.esp0 = (DWord) &(t->stack[KERNEL_STACK_SIZE]);
     
  /* Update Page Table */
  /* Copiem a cada espai de dades la informacio del process que estava a memoria fisica */
  for(i=0; i<NUM_PAG_DATA; i++)
  {
    set_ss_pag((unsigned)(PAG_LOG_INIT_DATA_P0+i), (unsigned)(t->task.data_frames[i]));
  }
     
  /* Flush TLB */
  set_cr3();
     
  /* Update Kernel stack */
  /* %esp apunta al final de la pila de kernel */
  __asm__ __volatile__ (
    "movl %0, %%esp" 
    :
	  : "g"(tss.esp0)
  );


  /* EOI if the process had an interruption when we switch */
  if(set_eoi) {
    __asm__ ("movb $0x20, %al\n" "outb %al, $0x20");
    set_eoi=0;
  }
        
  /* Restore registers */
     __asm__ __volatile__ (
	/* Es posa a sobre de la pila de kernel */
        "subl $64, %esp \n"
        "popl %ebx\n"
        "popl %ecx\n"
        "popl %edx\n"
        "popl %esi\n"
        "popl %edi\n"
        "popl %ebp\n"
        "popl %eax\n"
        "popl %ds\n"
        "popl %es\n"
        "popl %fs\n"
        "popl %gs \n"
    );
     /* Return */
     __asm__ __volatile__ ("iret");
     
}

void init_queues()
{
  int i;
  /* inicialitzem la llista de lliures i la de run */
  INIT_LIST_HEAD(&runqueue);
  INIT_LIST_HEAD(&freequeue);
  INIT_LIST_HEAD(&keyboardqueue);  

  /* incloem totes les task slots com lliures */
  for (i=1; i<NR_TASKS; i++)
    list_add(&(task[i].t.task.p_list), &freequeue);
}

void init_semaphores()
{
  int i;
  /* Posem tots el semafors com no inicialitzats */
  for (i=0; i<NR_SEM; i++) 
    sems[i].init = UNINITIALIZED;   
}

union task_union * get_free_task()
{
  union task_union * t;
  /* Comprovem si la llista de task lliures es buida */
  if (list_empty(&freequeue)) return NULL;
  /* Si no ho es, agafem el primer */
  t = (union task_union *)list_head_to_task_struct(freequeue.next);
  list_del(freequeue.next);
  return t;
}

void init_channel(const char * path,int flags, int ch)
{
  struct logic_device * t;
  struct TFA_item * it; 
  t = search_logic_device(path);
  it = get_TFA_free_entry();
  it->nref =1;
  it->seq_pos = 0;
  it->acces_mode = flags;  /*Guardem nomes permisos O_RDONLY, O_WRONLY o O_RDWR*/
  it->file = t;
  /* actualitzem el task struct del proces que l'executa */
  current()->channels[ch] = it;
}

void init_task0(void)
{
  int i;
/* Initializes paging for the process 0 adress space */
  init_queues();
  init_semaphores();
  set_user_pages(&task[INIT].t.task);
  set_cr3();
  
  task[INIT].t.task.Pid  = 0; 
  next_pid = 0;
  /*Posem el parametres al proces 0 */
  task[INIT].t.task.quantum        = DEFAULT_QUANTUM;
  task[INIT].t.task.remaining_life = DEFAULT_QUANTUM;
  remaining_tics                  = DEFAULT_QUANTUM;
  task[INIT].t.task.tics = 0;
  task[INIT].t.task.cs = 0;
  for (i = 0; i < NR_CHANNELS;++i)
    task[INIT].t.task.channels[i]= NULL;
  init_channel("KEYBOARD",O_RDONLY,0);  /* STDIN */
  init_channel("DISPLAY",O_WRONLY,1);   /* STDOUT */
  init_channel("DISPLAY",O_WRONLY,2);   /* STDERR */
  /* i l'afegim a la cua de run */
  list_add_tail(&(task[INIT].t.task.p_list), &runqueue);    
}

void scheduler()
{
  union task_union *t;
  current()->remaining_life--; /* reduïm la vida del proces actual */
  remaining_tics--;            /* reduim la variable remaining_tics (requeriment de l'enunciat)*/
  current()->tics++;           /* augmentem el total de tics que el proces es a la cpu*/
  if(current()->remaining_life <= 0) /* Si se li acaba la vida */
  {
	  current()->remaining_life = current()->quantum; /* Restaurem el quantum */
	  if (!list_is_last(runqueue.next, &runqueue)) /* Si no es l'ultim canviem el context */
	  {
	    list_del(&(current()->p_list));
	    list_add_tail(&(current()->p_list), &runqueue);
	    t = (union task_union *) list_head_to_task_struct(runqueue.next);
      t->task.cs++; /* augmentem el nombre de canvis de context */
	    task_switch(t);
	  }
  }
}

//bloqueja el proces actual a la cua queue i passa a run el seguent proces de la runqueue
void block_task(struct list_head * queue)
{
  union task_union *t;
   
  list_del(&(current()->p_list));
  list_add_tail(&(current()->p_list), queue);  /* el posa a la nova cua */
  current()->remaining_life = current()->quantum;
  
  t = (union task_union *) list_head_to_task_struct(runqueue.next);  
  t->task.cs++;  /* canviem el contexte */
  
  task_switch(t);
}


struct task_struct * get_task_struct_by_pid(int pid) 
{
  int i;
  struct task_struct * aux;
  struct list_head * pointer;
  //primer busquem a la cua de ready
  pointer = runqueue.next;
  while (pointer != &runqueue)
  {
    aux = list_head_to_task_struct(pointer);
    if (aux->Pid == pid) return aux;
    pointer = pointer->next;
  }
  /* despres a la cua de bloquejats pel teclat */
  pointer = keyboardqueue.next;
  while (pointer != &keyboardqueue)
  {
    aux = list_head_to_task_struct(pointer);
    if (aux->Pid == pid) return aux;
    pointer = pointer->next;
  }
  //despres busquem a les dels semafors
  for (i =0; i < NR_SEM;++i)
  {
    if (sems[i].init == INITIALIZED) 
    {
      pointer = sems[i].queue.next;
      while (pointer != &sems[i].queue)
      {
        aux = list_head_to_task_struct(pointer);
        if (aux->Pid == pid) return aux;
        pointer = pointer->next;
      }
    }    
  }
  return NULL;
}

int get_channel()
{
  int i;
  for (i=0; i<NR_CHANNELS; i++)
    if (current()->channels[i]==NULL) return i;  
  return -1;
}


